﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Net;
using System.Runtime.Serialization;
using System.ComponentModel;

namespace WebPALTT.Lib
{
    // This file defines the abstract class ActionItem as well as each of the classes that inherits from ActionItem.
    // Each of the classes that inherit from ActionItem represents a seperate type of action item in the scenario.
    // The file also contains a collection class for ActionItem objects 
    // and the ActionResults definition which is used by ActionItem to store the results of each action.


    /*
     * Class: ActionItem
     * 
     * An abstract class that is used as the generic form of all action item types.
     * This class defines the Name, Type and Results of the action.
     * 
     * + Type is an abstract property that is implemented seperately in each action item.
     *   Calling ActionItem.ActionType will return an enum of ActionType which can be used to determin the actual type of the action.
     */
    [Serializable]
    public abstract class ActionItem : ISerializable
    {
        Guid mKey;
        String mName;
        ActionResults mResults;
        ActionItem mParent = null;
        int mIndex = -1;

        /*
         * Name: Properties
         * 
         * {{/string/ Name}}:
         * 
         * The name of the action.
         * 
         * /ActionType/ ActionType:
         * 
         * This is an abstract property that is implemented seperately in each action item.
         * Calling ActionItem.ActionType will return an enum of ActionType which can be used to determin the actual type of the action.
         * 
         * /@link[ActionResults.cs.htm, ActionResults]/ Results:
         * 
         * Gets or sets the results of the action as an object of type @link [ActionItem.cs.htm, ActionResult]
         */
        /// <summary>
        /// Gets or sets the name of the action.
        /// </summary>
        [CategoryAttribute("Action Properties")]
        public String Name { get { return mName; } set { mName = value; } }
        /// <summary>
        /// Gets the type of the action. (returned as an ActionType enum)
        /// </summary>
        [CategoryAttribute("Action Properties"), ReadOnlyAttribute(true)]
        public abstract ActionType ActionType { get; }
        
        [ReadOnlyAttribute(true), BrowsableAttribute(false)]
        public ActionItem Parent { get { return mParent; } }
        [ReadOnlyAttribute(true), BrowsableAttribute(false)]
        public int Index { get { return mIndex; } }
        [ReadOnlyAttribute(true), BrowsableAttribute(false)]
        public Guid Key { get { return mKey; } }
        /// <summary>
        /// Gets or sets the results of the action.
        /// </summary>
        [ReadOnlyAttribute(true), BrowsableAttribute(false)]
        public ActionResults Results { get { return mResults; } set { mResults = value; } }

        public ActionItem()
        { this.mKey = Guid.NewGuid(); }

        public ActionItem(String name) : this()
        { mName = name; }

        public ActionItem(SerializationInfo info, StreamingContext ctxt)
        {
            mName = (string)info.GetValue("Name", typeof(string));
            mResults = (ActionResults)info.GetValue("Results", typeof(ActionResults));
            try
            {
                mKey = (Guid)info.GetValue("Key", typeof(Guid));
            }
            catch (SerializationException) { mKey = Guid.NewGuid(); }
        }

        public virtual void GetObjectData(SerializationInfo info, StreamingContext ctxt)
        {
            info.AddValue("Name", mName);
            info.AddValue("Results", mResults);
            info.AddValue("Key", mKey);
        }

        /// <summary>
        /// Validates the action, returning a list of string containing any validation errors.
        /// </summary>
        /// <returns>validation errors</returns>
        public abstract string[] Validate();

        public virtual void SetParent(ActionItem parent, int index = 0)
        {
            mParent = parent;
            if (index < 0)
                throw new IndexOutOfRangeException("Index must be greater than or equal to zero.");
            mIndex = index;
        }

        public void SetIndex(int index)
        {
            if (index < 0)
                throw new IndexOutOfRangeException("Index must be greater than or equal to zero.");
            mIndex = index;
        }

        public abstract ActionItem GetChild(Guid key);
    }

    /*
     * Type: ActionType
     * 
     * An enum that defines the different types of actions available.
     * 
     * Values:
     * 
     * + WebRequest
     * + Script
     * + Sleep
     * + If
     * + Loop
     * + Variable
     */
    public enum ActionType { WebRequest, Script, Sleep, If, Loop, Variable }    

    class ActionItem_HttpRequest
    {
        private HttpWebRequest mRequest;
        private String mContent;

        #region Properties
        /// <summary>
        /// Gets or set the request object used.
        /// </summary>
        public HttpWebRequest Request
        {
            get { return mRequest; }
            set { mRequest = value; }
        }

        /// <summary>
        /// Gets or set the body of the http request (only available for POST requests). 
        /// </summary>
        public String Content
        {
            get { return mContent; }
            set { mContent = value; }
        }
        #endregion

        /// <summary>
        /// Creates a new http web request action item based on an existing web definition.
        /// </summary>
        /// <param name="rawRequest"></param>
        public ActionItem_HttpRequest(ActionItem_WebRequest rawRequest)
        {
            mContent = rawRequest.Content;

            mRequest = (HttpWebRequest)WebRequest.Create(rawRequest.Uri);
            mRequest.Method = rawRequest.Method;
            mRequest.ProtocolVersion = rawRequest.HttpVersion.Equals("1.1") ? HttpVersion.Version11 : HttpVersion.Version10;
            mRequest.AllowAutoRedirect = false;
            mRequest.ContentLength = mContent == null ? 0 : mContent.Length;
            foreach (KeyValuePair<String, String> head in rawRequest.Headers)
            {
                if (head.Key.ToLower().Equals("accept"))
                    mRequest.Accept = head.Value;
                else if (head.Key.ToLower().Equals("connection"))
                    if (head.Value.ToLower().Equals("keep-alive"))
                        mRequest.KeepAlive = true;
                    else if (head.Value.ToLower().Equals("close"))
                        mRequest.KeepAlive = false;
                    else
                        mRequest.Connection = head.Value;
                else if (head.Key.ToLower().Equals("content-length"))
                    continue;
                else if (head.Key.ToLower().Equals("content-type"))
                    mRequest.ContentType = head.Value;
                else if (head.Key.ToLower().Equals("expect")) { }
                else if (head.Key.ToLower().Equals("date")) { }
                else if (head.Key.ToLower().Equals("host"))
                    mRequest.Host = head.Value;
                else if (head.Key.ToLower().Equals("if-modified-since")) { }
                else if (head.Key.ToLower().Equals("range")) { }
                else if (head.Key.ToLower().Equals("referer"))
                    mRequest.Referer = head.Value;
                else if (head.Key.ToLower().Equals("user-agent"))
                    mRequest.UserAgent = head.Value;
                else
                    mRequest.Headers.Add(head.Key, head.Value);
            }
        }
    }
}